﻿//
// MIDI Module driver
// 2012 Pete Brown
// Apache 2.0 License. Source and license at http://petebrown.codeplex.com
// My site and blog: http://10rem.net 
//

using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
using System.IO.Ports;

namespace Gadgeteer.Modules.PeteBrown
{


    /// <summary>
    /// A MidiModule module for Microsoft .NET Gadgeteer
    /// </summary>
    public sealed partial class MidiModule : GTM.Module
    {
        private GTI.Serial _serial;

        // noticed that gadgeteer doesn't use EventArgs-derived classes for second param in 
        // an event delegate. Saves some extra object creation, so I'll follow suit.
        /// <summary>
        /// Delegate for the MidiMessageReceived event.
        /// </summary>
        /// <param name="sender">this</param>
        /// <param name="message">The MIDI message that was received</param>
        public delegate void MidiMessageReceivedEventHandler(MidiModule sender, MidiMessage message);

        /// <summary>
        /// Fires whenever a MIDI message is received. Don't do anything too time consuming in here, or
        /// you'll tie up the MIDI processing loop.
        /// </summary>
        public event MidiMessageReceivedEventHandler MessageReceived;

        //public MidiMessageReceivedEventHandler MessageReceived;


        /// <summary>Create a new MIDI module at the specified socket</summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public MidiModule(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            // need a UART/USART socket.
            if (!socket.SupportsType('U'))
            {
                throw new GT.Socket.InvalidSocketException("Socket '" + socketNumber + "' does not support the MIDI module. Please plug the MIDI module into a socket labeled 'U'.");
            }

            // initialize serial comms
            _serial = new GTI.Serial(
                socket,
                31250,
                GTI.Serial.SerialParity.None,
                GTI.Serial.SerialStopBits.One,
                8,
                GTI.Serial.HardwareFlowControl.NotRequired,
                this);

            ActiveSenseMode = MidiActiveSenseMode.AutomaticallyRespond;
            FilterToChannel = MidiChannel.ChannelOmni;

            AutoFlushSerialOutput = true;

            // open ze port
            _serial.Open();
            _serial.AutoReadLineEnabled = false;
        }



        private void DispatchMessage(MidiMessage message)
        {
            // raise event

            // TODO: Should do this asynchronously so we don't tie up the processing with long-running user code

            //DebugPrint("MIDI Message: " + message.ID.ToString());

            if (MessageReceived != null)
                if (FilterToChannel == MidiChannel.ChannelOmni || message.Channel == FilterToChannel)
                    MessageReceived(this, message);

        }

        private const int MaxBufferSize = 128;
        private byte[] _buffer = new byte[MaxBufferSize];
        
        // Loads incoming data from the serial port to the buffer.
        private void OnSerialDataReceived(GTI.Serial sender, SerialData data)
        {
            if (data == SerialData.Chars)
            {
                int count = 0;

                count = _serial.Read(_buffer, 0, _serial.BytesToRead > MaxBufferSize ? MaxBufferSize : sender.BytesToRead);

                // performance of this needs to be monitored to see if
                // it holds up with other tasks going on
                if (SoftThru)
                {
                    _serial.Write(_buffer, 0, count);
                    _serial.Flush();
                }

                    ParseBuffer(count);
                //}

            }
            else if (data == SerialData.Eof)
            {
                // TODO
            }
        }

    }
}
